package com.example.socket.handler;

import com.example.socket.anno.SocketCommand;
import com.example.socket.anno.SocketModule;
import com.example.socket.codec.Coder;
import com.example.socket.codec.MessageConvertor;
import com.example.socket.core.Command;
import com.example.socket.exception.ProcessorNotFound;
import com.example.socket.exception.TypeDefinitionNotFound;
import io.netty.util.internal.ConcurrentSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.helpers.FormattingTuple;
import org.slf4j.helpers.MessageFormatter;

import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 指令注册器
 * @author frank
 */
public class CommandRegister implements Cloneable {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    /** 指令->指令信息，映射表 */
    private ConcurrentHashMap<Command, CommandInfo> infos = new ConcurrentHashMap<>();
    /** 已注册接口 */
    private ConcurrentSet<Class<?>> interfaces = new ConcurrentSet<>();

    /** 参数构造器 */
    private ParameterBuilder builder;
    /** BEAN NAME */
    private String name;

    private Map<Byte, Coder> coders;

    /**
     * 构造方法
     * @param convertor 消息转换器
     */
    public CommandRegister(MessageConvertor convertor) {
        this("DEFAULT", new ParameterBuilder(convertor), new ConcurrentHashMap<>());
    }

    /**
     * 构造方法
     * @param convertor 消息转换器
     */
    public CommandRegister(String name, MessageConvertor convertor) {
        this(name, new ParameterBuilder(convertor), new ConcurrentHashMap<>());
    }

    public CommandRegister(String name, ParameterBuilder builder) {
        this(name, builder, new ConcurrentHashMap<>());
    }

    /**
     * 私有构造方法
     * @param builder 参数构造器
     * @param infos 指令信息
     */
    private CommandRegister(String name, ParameterBuilder builder, Map<Command, CommandInfo> infos) {
        this.name = name;
        this.builder = builder;
        this.infos = new ConcurrentHashMap<>(infos);
        this.coders = builder.getConvertor().getCoders();
        for (CommandInfo commandInfo : infos.values()) {
            callback(commandInfo);
        }
    }

    /**
     * 注册指令{@link Command}对应的处理器{@link Processor}和消息体定义{@link TypeDefinition}
     * @param command 指令
     * @param definition 消息体定义
     * @param processor 消息处理器
     * @throw {@link IllegalStateException} 重复注册时会抛出
     */
    public void register(Command command, TypeDefinition definition, Processor<?, ?> processor) {
        CommandInfo info = new CommandInfo(command, definition, processor);

        CommandInfo prev = infos.putIfAbsent(info.getCommand(), info);
        if (prev != null) {
            FormattingTuple message = MessageFormatter.arrayFormat("[{}] - 指令[{}] - {} 重复注册:{}", new Object[]{name,
                    command, processor, prev.getProcessor()});
            logger.error(message.getMessage());
            throw new IllegalStateException(message.getMessage());
        }
        callback(info);
        if (logger.isDebugEnabled()) {
            logger.debug("[{}] - 完成指令注册:{}", name, info);
        }
    }

    /**
     * 注册通讯接口/类
     * @param clz
     */
    public void registerInterface(Class<?> clz) {
        if (interfaces.contains(clz)) {
            // 已注册
            return;
        }
        SocketModule handler = clz.getAnnotation(SocketModule.class);
        for (Method method : clz.getMethods()) {
            if (method.getAnnotation(SocketCommand.class) == null) {
                continue; // 忽略无效的方法
            }
            SocketCommand socketCommand = method.getAnnotation(SocketCommand.class);
            // 创建指令对象
            Command command = Command.valueOf(handler.value(), socketCommand.value());
            // 创建类型定义
            MethodDefinition definition = MethodDefinition.valueOf(handler.format(), clz, method, builder);
            if (infos.contains(command)) {
                if (logger.isDebugEnabled()) {
                    logger.debug("[{}] - 推送指令已注册:{}", name, command);
                }
                continue;
            }
            // 进行注册
            CommandInfo info = new CommandInfo(command, definition, null);
            CommandInfo prev = infos.putIfAbsent(info.getCommand(), info);
            if (prev != null) {
                if (logger.isDebugEnabled()) {
                    logger.debug("[{}] - 推送指令已注册:{}", name, command);
                }
                continue;
            }
            callback(info);
            if (logger.isDebugEnabled()) {
                logger.debug("[{}] - 完成指令注册:{}", name, info);
            }
        }
    }

    /**
     * 移除已经注册的指令
     * @param command 指令
     */
    public void unregister(Command command) {
        infos.remove(command);
    }

    /**
     * 检查是否包含指定的指令
     * @param command 指令
     * @return
     */
    public boolean contain(Command command) {
        return infos.contains(command);
    }

    /**
     * 获取对应的{@link CommandInfo}
     * @param command 指令
     * @return 指定对应的指令信息
     */
    public CommandInfo getCommand(Command command) {
        CommandInfo info = infos.get(command);
        return info;
    }

    /**
     * 获取对应的{@link Processor}
     * @param command 指令
     * @return 指定对应的处理器，不会返回null
     * @throws ProcessorNotFound 处理器不存在时抛出
     */
    public Processor<?, ?> getProcessor(Command command) {
        CommandInfo info = getCommand(command);
        if (info == null || !info.hasProcessor()) {
            FormattingTuple message = MessageFormatter.format("[{}] - 指令[{}]对应的业务处理器不存在", name, command);
            if (logger.isDebugEnabled()) {
                logger.debug(message.getMessage());
            }
            throw new ProcessorNotFound(message.getMessage());
        }
        return info.getProcessor();
    }

    /**
     * 获取对应的{@link TypeDefinition}
     * @param command 指令
     * @return 指定对应的消息体定义，不会返回null
     * @throws TypeDefinitionNotFound 消息体不存在时抛出
     */
    public TypeDefinition getDefinition(Command command) {
        CommandInfo info = getCommand(command);
        if (info == null) {
            FormattingTuple message = MessageFormatter.format("[{}] - 指令[{}]对应的消息体类型定义不存在", name, command);
            if (logger.isDebugEnabled()) {
                logger.debug(message.getMessage());
            }
            throw new TypeDefinitionNotFound(message.getMessage());
        }
        return info.getDefinition();
    }

    /**
     * 获取全部已经注册的指令集合
     * @return
     */
    public Set<Command> getCommands() {
        return new HashSet<>(infos.keySet());
    }

    @Override
    public CommandRegister clone() {
        return new CommandRegister(name, builder, this.infos);
    }

    public void callback(CommandInfo commandInfo) {
        byte type = commandInfo.getDefinition().format;
        Coder coder = coders.get(type);
        if (coder != null) {
            coder.afterRegister(commandInfo);
        }
    }
}